// ##########################################################################
// #                                                                        #
// #                              CLOUDCOMPARE                              #
// #                                                                        #
// #  This program is free software; you can redistribute it and/or modify  #
// #  it under the terms of the GNU General Public License as published by  #
// #  the Free Software Foundation; version 2 or later of the License.      #
// #                                                                        #
// #  This program is distributed in the hope that it will be useful,       #
// #  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
// #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          #
// #  GNU General Public License for more details.                          #
// #                                                                        #
// #          COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI)             #
// #                                                                        #
// ##########################################################################

#include "ccGuiParameters.h"

// Qt
#include <QSettings>

// qCC_db
#include <ccBasicTypes.h>
#include <ccSingleton.h>

// System
#include <string.h>

//! Unique instance of ccGui
static ccSingleton<ccGui> s_gui;

const ccGui::ParamStruct& ccGui::Parameters()
{
	if (!s_gui.instance)
	{
		s_gui.instance = new ccGui();
		s_gui.instance->params.fromPersistentSettings();
	}

	return s_gui.instance->params;
}

void ccGui::ReleaseInstance()
{
	s_gui.release();
}

void ccGui::Set(const ParamStruct& params)
{
	if (!s_gui.instance)
	{
		s_gui.instance = new ccGui();
		s_gui.instance->params.fromPersistentSettings();
	}

	s_gui.instance->params = params;
}

ccGui::ParamStruct::ParamStruct()
{
	reset();
}

void ccGui::ParamStruct::reset()
{
	lightAmbientColor  = ccColor::night;
	lightSpecularColor = ccColor::darker;
	lightDiffuseColor  = ccColor::bright;
	meshFrontDiff      = ccColor::defaultMeshFrontDiff;
	meshBackDiff       = ccColor::defaultMeshBackDiff;
	meshSpecular       = ccColor::middle;
	pointsDefaultCol   = ccColor::defaultColor;
	textDefaultCol     = ccColor::defaultColor;
	backgroundCol      = ccColor::defaultBkgColor;
	labelBackgroundCol = ccColor::defaultLabelBkgColor;
	labelMarkerCol     = ccColor::defaultLabelMarkerColor;
	bbDefaultCol       = ccColor::yellow;

	lightDoubleSided       = true;
	drawBackgroundGradient = true;
	drawRoundedPoints      = false;
	decimateMeshOnMove     = true;
	minLoDMeshSize         = 2500000;
	decimateCloudOnMove    = true;
	minLoDCloudSize        = 50000000;
	useVBOs                = true;
	displayCross           = true;
	pickingCursorShape     = Qt::CrossCursor;
	logVerbosityLevel      = ccLog::LOG_STANDARD;

	labelMarkerSize = 5;

	colorScaleShowHistogram   = true;
	colorScaleUseShader       = false;
	colorScaleShaderSupported = false;
	colorScaleRampWidth       = 50;

#ifdef Q_OS_MAC
	defaultFontSize = 12;
	labelFontSize   = 10;
#else
	defaultFontSize = 10;
	labelFontSize   = 8;
#endif

	displayedNumPrecision = 6;
	labelOpacity          = 75;

	zoomSpeed = 1.0;

	autoComputeOctree = ASK_USER;

	singleClickPicking = true;
}

static int c_fColorArraySize   = sizeof(float) * 4;
static int c_ub3ColorArraySize = sizeof(unsigned char) * 3;
static int c_ub4ColorArraySize = sizeof(unsigned char) * 4;

void ccGui::ParamStruct::fromPersistentSettings()
{
	QSettings settings;
	settings.beginGroup("OpenGL");
	lightAmbientColor  = ccColor::Rgbaf(reinterpret_cast<float*>(settings.value("lightAmbientColor", QByteArray::fromRawData((const char*)ccColor::darkest.rgba, c_fColorArraySize)).toByteArray().data()));
	lightSpecularColor = ccColor::Rgbaf(reinterpret_cast<float*>(settings.value("lightSpecularColor", QByteArray::fromRawData((const char*)ccColor::darker.rgba, c_fColorArraySize)).toByteArray().data()));
	lightDiffuseColor  = ccColor::Rgbaf(reinterpret_cast<float*>(settings.value("lightDiffuseColor", QByteArray::fromRawData((const char*)ccColor::bright.rgba, c_fColorArraySize)).toByteArray().data()));
	meshFrontDiff      = ccColor::Rgbaf(reinterpret_cast<float*>(settings.value("meshFrontDiff", QByteArray::fromRawData((const char*)ccColor::defaultMeshFrontDiff.rgba, c_fColorArraySize)).toByteArray().data()));
	meshBackDiff       = ccColor::Rgbaf(reinterpret_cast<float*>(settings.value("meshBackDiff", QByteArray::fromRawData((const char*)ccColor::defaultMeshBackDiff.rgba, c_fColorArraySize)).toByteArray().data()));
	meshSpecular       = ccColor::Rgbaf(reinterpret_cast<float*>(settings.value("meshSpecular", QByteArray::fromRawData((const char*)ccColor::middle.rgba, c_fColorArraySize)).toByteArray().data()));
	pointsDefaultCol   = ccColor::Rgbaub(reinterpret_cast<unsigned char*>(settings.value("pointsDefaultRGBAColor", QByteArray::fromRawData((const char*)ccColor::defaultColor.rgba, c_ub4ColorArraySize)).toByteArray().data()));
	textDefaultCol     = ccColor::Rgbaub(reinterpret_cast<unsigned char*>(settings.value("textDefaultRGBAColor", QByteArray::fromRawData((const char*)ccColor::defaultColor.rgba, c_ub4ColorArraySize)).toByteArray().data()));
	backgroundCol      = ccColor::Rgbaub(reinterpret_cast<unsigned char*>(settings.value("backgroundColor", QByteArray::fromRawData((const char*)ccColor::defaultBkgColor.rgb, c_ub3ColorArraySize)).toByteArray().data()));
	labelBackgroundCol = ccColor::Rgbaub(reinterpret_cast<unsigned char*>(settings.value("labelBackgroundRGBAColor", QByteArray::fromRawData((const char*)ccColor::defaultLabelBkgColor.rgba, c_ub4ColorArraySize)).toByteArray().data()));
	labelMarkerCol     = ccColor::Rgbaub(reinterpret_cast<unsigned char*>(settings.value("labelMarkerRGBAColor", QByteArray::fromRawData((const char*)ccColor::defaultLabelMarkerColor.rgba, c_ub4ColorArraySize)).toByteArray().data()));
	bbDefaultCol       = ccColor::Rgbaub(reinterpret_cast<unsigned char*>(settings.value("bbDefaultRGBAColor", QByteArray::fromRawData((const char*)ccColor::yellow.rgba, c_ub4ColorArraySize)).toByteArray().data()));

	lightDoubleSided        = settings.value("lightDoubleSided", true).toBool();
	drawBackgroundGradient  = settings.value("backgroundGradient", true).toBool();
	drawRoundedPoints       = settings.value("drawRoundedPoints", false).toBool();
	decimateMeshOnMove      = settings.value("meshDecimation", true).toBool();
	minLoDMeshSize          = settings.value("minLoDMeshSize", 2500000).toUInt();
	decimateCloudOnMove     = settings.value("cloudDecimation", true).toBool();
	minLoDCloudSize         = settings.value("minLoDCloudSize", 50000000).toUInt();
	useVBOs                 = settings.value("useVBOs", true).toBool();
	displayCross            = settings.value("crossDisplayed", true).toBool();
	labelMarkerSize         = static_cast<unsigned>(std::max(0, settings.value("labelMarkerSize", 5).toInt()));
	colorScaleShowHistogram = settings.value("colorScaleShowHistogram", true).toBool();
	colorScaleUseShader     = settings.value("colorScaleUseShader", false).toBool();
	// colorScaleShaderSupported	= not saved
	colorScaleRampWidth   = static_cast<unsigned>(std::max(0, settings.value("colorScaleRampWidth", 50).toInt()));
	defaultFontSize       = static_cast<unsigned>(std::max(0, settings.value("defaultFontSize", 10).toInt()));
	labelFontSize         = static_cast<unsigned>(std::max(0, settings.value("labelFontSize", 8).toInt()));
	displayedNumPrecision = static_cast<unsigned>(std::max(0, settings.value("displayedNumPrecision", 6).toInt()));
	labelOpacity          = static_cast<unsigned>(std::max(0, settings.value("labelOpacity", 75).toInt()));
	zoomSpeed             = settings.value("zoomSpeed", 1.0).toDouble();
	autoComputeOctree     = static_cast<ComputeOctreeForPicking>(settings.value("autoComputeOctree", ASK_USER).toInt());
	singleClickPicking    = settings.value("singleClickPicking", true).toBool();
	pickingCursorShape    = static_cast<Qt::CursorShape>(settings.value("cursorShape", static_cast<int>(Qt::CrossCursor)).toInt());
	logVerbosityLevel     = static_cast<ccLog::MessageLevelFlags>(settings.value("logVerbosityLevel", static_cast<int>(ccLog::LOG_STANDARD)).toInt());

	settings.endGroup();
}

void ccGui::ParamStruct::toPersistentSettings() const
{
	QSettings settings;
	settings.beginGroup("OpenGL");

	settings.setValue("lightDiffuseColor", QByteArray((const char*)lightDiffuseColor.rgba, c_fColorArraySize));
	settings.setValue("lightAmbientColor", QByteArray((const char*)lightAmbientColor.rgba, c_fColorArraySize));
	settings.setValue("lightSpecularColor", QByteArray((const char*)lightSpecularColor.rgba, c_fColorArraySize));
	settings.setValue("meshFrontDiff", QByteArray((const char*)meshFrontDiff.rgba, c_fColorArraySize));
	settings.setValue("meshBackDiff", QByteArray((const char*)meshBackDiff.rgba, c_fColorArraySize));
	settings.setValue("meshSpecular", QByteArray((const char*)meshSpecular.rgba, c_fColorArraySize));
	settings.setValue("pointsDefaultRGBAColor", QByteArray((const char*)pointsDefaultCol.rgba, c_ub4ColorArraySize));
	settings.setValue("textDefaultRGBAColor", QByteArray((const char*)textDefaultCol.rgba, c_ub4ColorArraySize));
	settings.setValue("backgroundColor", QByteArray((const char*)backgroundCol.rgb, c_ub3ColorArraySize));
	settings.setValue("labelBackgroundRGBAColor", QByteArray((const char*)labelBackgroundCol.rgba, c_ub4ColorArraySize));
	settings.setValue("labelMarkerRGBAColor", QByteArray((const char*)labelMarkerCol.rgba, c_ub4ColorArraySize));
	settings.setValue("bbDefaultRGBAColor", QByteArray((const char*)bbDefaultCol.rgba, c_ub4ColorArraySize));
	settings.setValue("backgroundGradient", drawBackgroundGradient);
	settings.setValue("drawRoundedPoints", drawRoundedPoints);
	settings.setValue("meshDecimation", decimateMeshOnMove);
	settings.setValue("minLoDMeshSize", minLoDMeshSize);
	settings.setValue("cloudDecimation", decimateCloudOnMove);
	settings.setValue("minLoDCloudSize", minLoDCloudSize);
	settings.setValue("useVBOs", useVBOs);
	settings.setValue("crossDisplayed", displayCross);
	settings.setValue("labelMarkerSize", labelMarkerSize);
	settings.setValue("colorScaleShowHistogram", colorScaleShowHistogram);
	settings.setValue("colorScaleUseShader", colorScaleUseShader);
	// settings.setValue("colorScaleShaderSupported", not saved);
	settings.setValue("colorScaleRampWidth", colorScaleRampWidth);
	settings.setValue("defaultFontSize", defaultFontSize);
	settings.setValue("labelFontSize", labelFontSize);
	settings.setValue("displayedNumPrecision", displayedNumPrecision);
	settings.setValue("labelOpacity", labelOpacity);
	settings.setValue("zoomSpeed", zoomSpeed);
	settings.setValue("autoComputeOctree", static_cast<int>(autoComputeOctree));
	settings.setValue("singleClickPicking", singleClickPicking);
	settings.setValue("cursorShape", static_cast<int>(pickingCursorShape));
	settings.setValue("logVerbosityLevel", static_cast<int>(logVerbosityLevel));

	settings.endGroup();
}

bool ccGui::ParamStruct::isInPersistentSettings(QString paramName) const
{
	QSettings settings;
	settings.beginGroup("OpenGL");
	return settings.contains(paramName);
}
